#include <stdio.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
#include <string.h>

#include "list.h"

#define BUF_LEN 8

// usb mouse结构体
struct usb_mouse_inf {
    int inter_in_endpoint;
    int interface_num;
    libusb_device_handle *dev_handle;
    struct libusb_transfer *transfer;
    libusb_device *device;
    struct list_head k_list;
    unsigned char buffer[BUF_LEN];
};

// 获取所有的usb mouse的信息
int get_usb_mouse_inf(libusb_device **devs, int count, struct usb_mouse_inf *mouse_list)
{
    int ret = 0;
    int interface_num = 0;
    int inter_in_endpoint = 0;
    int mouse_count = 0;
    struct libusb_device_descriptor desc = {0};
    libusb_device *device = NULL;
    struct libusb_config_descriptor *config_desc = NULL;
    struct libusb_interface_descriptor *intf_desc = NULL;
    libusb_device_handle *dev_handle = NULL;
    struct usb_mouse_inf *p_mouse_inf = NULL;

    for (int i = 0; i < count; i++) {
        device = devs[i];

        // 获取USB设备描述符
        ret = libusb_get_device_descriptor(device, &desc);
        if (ret < 0) {
            fprintf(stderr, "Error getting device descriptor %d %s\n", i, libusb_strerror(ret));
            continue;
        }
        printf("Vendor ID: 0x%04x, Product ID: 0x%04x\n", desc.idVendor, desc.idProduct);
        printf("NumConfigurations: 0x%02x\n", desc.bNumConfigurations);
    }

    for (int i = 0; i < count; i++) {
        device = devs[i];

        // 获取USB的配置描述符,第一次插入时候默认使用配置0
        ret = libusb_get_config_descriptor(device, 0, &config_desc);
        if (ret) {
            fprintf(stderr, "Error getting device config descriptor %s\n", libusb_strerror(ret));
            return -1;
        }

        // 获取接口描述符
        for (int interface = 0; interface < config_desc->bNumInterfaces; interface++) {
            // 获取USB的配置描述符描述符,第一次插入时候默认使用配置0中的接口0
            intf_desc = &config_desc->interface[interface].altsetting[0];
            // 获取接口描述符中的个数
            interface_num = intf_desc->bInterfaceNumber;

            // 通过接口描述符,判断是否是鼠标
            /**
             * bInterfaceClass         3 Human Interface Device
             * bInterfaceSubClass      1 Boot Interface Subclass
             * bInterfaceProtocol      2 Mouse
            */
            if (intf_desc->bInterfaceClass != 3 || intf_desc->bInterfaceProtocol != 2) {
                // 继续查找鼠标
                continue;
            } else {
                // 找到鼠标
                printf("Find Usb Mouse\n");
                /**
                 * 查找USB端点
                 * 端点 1: 用于从主机PC接收的命令的Bulk Out端点
                 * 端点 2: 用于发送给主机PC的响应的Bulk In端点
                */
                /**
                 *  Endpoint Descriptor:
                 *    bLength                 7
                 *    bDescriptorType         5
                 *    bEndpointAddress     0x81  EP 1 IN
                 *    bmAttributes            3
                 *    Transfer Type            Interrupt
                 *    Synch Type               None
                 *    Usage Type               Data
                 *    wMaxPacketSize     0x0008  1x 8 bytes
                 *    bInterval               1
                */
                for (int i = 0; i < intf_desc->bNumEndpoints; i++) {
                    // 判断端点描述符的bmAttributes和bEndpointAddress
                    if ((intf_desc->endpoint[i].bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_INTERRUPT ||
                        (intf_desc->endpoint[i].bEndpointAddress & LIBUSB_ENDPOINT_IN) == LIBUSB_ENDPOINT_IN) {
                        //找到inter输入端点
                        inter_in_endpoint = intf_desc->endpoint[i].bEndpointAddress;
                        // 通过libusb_device device打开设备
                        ret = libusb_open(device, &dev_handle);
                        if (ret) {
                            fprintf(stderr, "Error libusb open devices %s\n", libusb_strerror(ret));
                            return -1;
                        }
                        // 分配结构体并初始化
                        p_mouse_inf = (struct usb_mouse_inf *)malloc(sizeof(struct usb_mouse_inf));
                        memset(p_mouse_inf, 0, sizeof(*p_mouse_inf));
                        // 保存device, dev_handle, inter_in_endpoint, interface_num信息
                        p_mouse_inf->device = device;
                        p_mouse_inf->dev_handle = dev_handle;
                        p_mouse_inf->inter_in_endpoint = inter_in_endpoint;
                        p_mouse_inf->interface_num = interface_num;
                        // mouse数量自加
                        mouse_count++;
                        // 添加到list中
                        list_add(&p_mouse_inf->k_list, &mouse_list->k_list);
                    }
                }  
            }
        }
        // 释放USB的配置描述符
        libusb_free_config_descriptor(config_desc);
    }

    // 没有找到USB鼠标
    if (mouse_count == 0) {
        return -1;
    }

    return mouse_count;
}

void mouse_callback(struct libusb_transfer *transfer)
{
    // 获取用户的数据
    struct usb_mouse_inf *p_mouse_inf = transfer->user_data;
    static int count = 0;
    // 判断标志位判断传输是否完成
    if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
    {
        /* parser data */
        printf("%04d datas: ", count++);
        // 根据实际的传输的位数来判断数据
        for (int i = 0; i < transfer->actual_length; i++)
        {
            printf("%02x ", transfer->buffer[i]);
        }
        printf("\n");
    }

    // 再次提交异步传输
    if (libusb_submit_transfer(transfer) < 0)
    {
        fprintf(stderr, "libusb_submit_transfer err\n");
    }
}

int main() {
    libusb_device **devs = NULL;
    libusb_context *ctx = NULL;
    int ret = 0;
    int count = 0;
    unsigned char buffer[16] = {0};
    int transferred = 0;
    int mouse_count = 0;
    struct timeval tv = {5, 0};

    struct usb_mouse_inf mouse_inf;
    struct usb_mouse_inf *p_mouse_inf = NULL;
    struct list_head *p_del_list_node = NULL;
    struct list_head *p_list_node = NULL;

    memset(&mouse_inf, 0, sizeof(struct usb_mouse_inf));
    INIT_LIST_HEAD(&mouse_inf.k_list);

    // libusb初始化
    ret = libusb_init(&ctx);
    if (ret < 0) {
        fprintf(stderr, "Error initializing libusb: %s\n", libusb_strerror(ret));
        return 1;
    }

    // 获取usb的列表,返回usb的个数
    count = libusb_get_device_list(ctx, &devs);
    if (count < 0) {
        fprintf(stderr, "Error getting device list: %s\n", libusb_strerror(count));
        libusb_exit(ctx);
        return 1;
    }

    printf("Number of USB devices: %d\n", count);

    mouse_count = get_usb_mouse_inf(devs, count, &mouse_inf);
    if (mouse_count < 0) {
        //释放USB列表
        libusb_free_device_list(devs, 1);
        libusb_exit(ctx);
        return 1;
    }

    //释放USB列表
    libusb_free_device_list(devs, 1);

    // 遍历mouse inf列表移除驱动
    list_for_each(p_list_node, &mouse_inf.k_list) {
        // 通过container_of获取数据
        p_mouse_inf = list_entry(p_list_node, struct usb_mouse_inf, k_list);
        // 使用libusb访问USB设备时,需要先移除(detach)设备原来的驱动程序,
        // 然后认领接口(claim interface)
        // 只是设置一个标记位表示libusb_claim_interface
        // 使用libusb_claim_interface时会detach原来的驱动
        libusb_set_auto_detach_kernel_driver(p_mouse_inf->dev_handle, 1);  

        // 标记这个interface已经被使用认领了
        ret = libusb_claim_interface(p_mouse_inf->dev_handle, p_mouse_inf->interface_num);
        if (ret) {
            fprintf(stderr, "Error libusb_claim_interface %s\n", libusb_strerror(ret));
            libusb_exit(ctx);
            return -1;
        }

        // 异步获取usb鼠标的数据
        // 申请异步传输
        p_mouse_inf->transfer = libusb_alloc_transfer(0);
        // 填充异步传输
        // 回调中处理USB数据
        libusb_fill_interrupt_transfer(p_mouse_inf->transfer, p_mouse_inf->dev_handle,\
            p_mouse_inf->inter_in_endpoint, p_mouse_inf->buffer, sizeof(p_mouse_inf->buffer),\
            mouse_callback, p_mouse_inf, 0);
        // 提交异步传输
        ret = libusb_submit_transfer(p_mouse_inf->transfer);
        if (ret < 0) {
            fprintf(stderr, "Error libusb_submit_transfer %s\n", libusb_strerror(ret));
            libusb_exit(ctx);
            return -1;
        }
    }

#if 0
    // 异步获取usb鼠标的数据
    // 遍历mouse inf列表
    list_for_each(p_list_node, &mouse_inf.k_list) {
        // 通过container_of获取数据
        p_mouse_inf = list_entry(p_list_node, struct usb_mouse_inf, k_list);
        // 申请异步传输
        p_mouse_inf->transfer = libusb_alloc_transfer(0);
        // 填充异步传输
        // 回调中处理USB数据
        libusb_fill_interrupt_transfer(p_mouse_inf->transfer, p_mouse_inf->dev_handle,\
            p_mouse_inf->inter_in_endpoint, p_mouse_inf->buffer, sizeof(p_mouse_inf->buffer),\
            mouse_callback, p_mouse_inf, 0);
        // 提交异步传输
        ret = libusb_submit_transfer(p_mouse_inf->transfer);
        if (ret < 0) {
            fprintf(stderr, "Error libusb_submit_transfer %s\n", libusb_strerror(ret));
            libusb_exit(ctx);
            return -1;
        }
    }
#endif

    // 阻塞方式处理任何"pengding的事件"
    while (1) {
        ret = libusb_handle_events_timeout_completed(NULL, &tv, NULL);
        if (ret < 0) {
            fprintf(stderr, "libusb_handle_events_timeout err\n");
            break;
        }
    }

    // 遍历mouse inf列表关闭handle节点
    list_for_each(p_list_node, &mouse_inf.k_list) {
        // 通过container_of获取数据
        p_mouse_inf = list_entry(p_list_node, struct usb_mouse_inf, k_list);
        /* libusb_close */
        libusb_release_interface(p_mouse_inf->dev_handle, p_mouse_inf->interface_num);
        // 关闭dev_handle
        libusb_close(p_mouse_inf->dev_handle);
    }

    // 释放mouse inf列表
    list_for_each_safe(p_list_node, p_del_list_node, &mouse_inf.k_list) {
        // 通过container_of获取数据
        p_mouse_inf = list_entry(p_list_node, struct usb_mouse_inf, k_list);
        // 删除节点
        list_del(&p_mouse_inf->k_list);
        // 释放空间
        free(p_mouse_inf);
    }

    //释放libusb
    libusb_exit(ctx);

    return 0;
}
